package com.taptap;

import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.HttpRequest;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class TapLoginManager {

    private static final Logger logger = LoggerFactory.getLogger("TapLoginManager");

    public static WebClient webClient = WebClient.create(Vertx.vertx(), new WebClientOptions().setKeepAlive(true));

    public static void main(String[] args) throws IOException {
        String client_id = "xgpiui142o8j5sfgyb";
        String kid = "1/geP58pBYZTOrTP8WlHylpbTDtYRsqVJn4JkuiagvToQfJIvSYtOrrU64RRx-GXiPuQ7eSB7pqG2OlDSRPfDDbu3qrScYBTsEnokLwVdJiVr0pkEj7Onej87JvWONqxAhhV1aOf80wOmejFFWJc0EZWlJDHrLX7aJ7U3QraXYtJ6rD5ZyRS6nzzgGNpmdRv2Vf6slGo2kz2YmjOYDdMYFXqk7sVyfVsF6WKWgdr9uMtRsZGPlthuHiDkRTGNWE0jeXvqe_k2IfEJYDfb1EsYPyVX28eXzvtqa9MQIPPLCa70EzU9QQc5gYS0SEl1wIvA7yNDdzcpdpZN0jmNvgtg6eQ"; // kid
        String mac_key = "lvZy66IpsUUr6eE2GVY3caOV9WCIvol15Ql0mWEc"; // mac_key
        String method = "GET";
//        String request_url = "https://openapi.taptap.com/account/profile/v1?client_id=" + client_id; //
//        String authorization = getAuthorization(request_url, method, kid, mac_key);
//        System.out.println(authorization);
//        URL url = new URL(request_url);
//        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//        // Http
//        conn.setRequestProperty("Authorization", authorization);
//        conn.setRequestMethod("GET");
//        BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
//        String line;
//        StringBuilder result = new StringBuilder();
//        while ((line = rd.readLine()) != null) {
//            result.append(line);
//        }
//        rd.close();
//        System.out.println(result.toString());
        checkToken(webClient, client_id, kid, mac_key);
    }

    public static Future<JsonObject> checkToken(WebClient webClient, String client_id, String access_token, String mac_key){
        String request_url = "https://openapi.taptap.com/account/basic-info/v1?client_id=" + client_id;
        String method = "GET";
        String authorization = getAuthorization(request_url, method, access_token, mac_key);
        Promise promise = Promise.promise();
        HttpRequest<Buffer> request = webClient.getAbs(request_url);
        request.putHeader("Authorization", authorization);
        request.timeout(10000l).send(res -> {
            if(res.succeeded()){
                HttpResponse<Buffer> response = res.result();
                /**
                 * 正常返回：{"data":{"openid":"vI7HV/v8RoZ33/QDrJTMmg==","unionid":"veRU33aQOhtPDtQ8/XtD3g=="},"now":1690852876,"success":true}
                 * 异常返回：{"data":{"code":-1,"msg":"未认证","error":"access_denied","error_description":"client not found. [client_id=xgpiui142o8j5sfgy]"},"now":1690895503,"success":false}
                 */
                JsonObject resObj = response.bodyAsJsonObject();
                promise.complete(resObj);
            }else {
                logger.error("taptap login checkToken error cause:{}", Tool.getException(res.cause()));
                promise.fail("");
            }
        });
        return promise.future();
    }

    /**
     * @param request_url
     * @param method "GET" or "POST"
     * @param key_id key id by OAuth 2.0
     * @param mac_key mac key by OAuth 2.0
     * @return authorization string
     */
    public static String getAuthorization(String request_url, String method, String key_id, String
            mac_key) {
        try {
            URL url = URI.create(request_url).toURL();
            String time = String.format(Locale.US, "%010d", System.currentTimeMillis() / 1000);
            String randomStr = getRandomString(16);
            String host = url.getHost();
            String uri = request_url.substring(request_url.lastIndexOf(host) + host.length());
            String port = "80";
            if (request_url.startsWith("https")) {
                port = "443";
            }
            String other = "";
            String sign = sign(mergeSign(time, randomStr, method, uri, host, port, other), mac_key);
            return "MAC " + getAuthorizationParam("id", key_id) + "," + getAuthorizationParam("ts", time)
                    + "," + getAuthorizationParam("nonce", randomStr) + "," + getAuthorizationParam("mac",
                    sign);
        } catch (MalformedURLException e) {
            logger.error("getAuthorization error exception:{}", Tool.getException(e));
        }
        return null;
    }
    private static String getRandomString(int length) {
        byte[] bytes = new byte[length];
        new SecureRandom().nextBytes(bytes);
        String base64String = Base64.getEncoder().encodeToString(bytes);
        return base64String;
    }
    private static String mergeSign(String time, String randomCode, String httpType, String uri,
                                    String domain, String port, String other) {
        if (time.isEmpty() || randomCode.isEmpty() || httpType.isEmpty() || domain.isEmpty() || port.isEmpty())
        {
            return null;
        }
        String prefix =
                time + "\n" + randomCode + "\n" + httpType + "\n" + uri + "\n" + domain + "\n" + port
                        + "\n";
        if (other.isEmpty()) {
            prefix += "\n";
        } else {
            prefix += (other + "\n");
        }
        return prefix;
    }
    private static String sign(String signatureBaseString, String key) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);
            byte[] text = signatureBaseString.getBytes(StandardCharsets.UTF_8);
            byte[] signatureBytes = mac.doFinal(text);
            signatureBytes = Base64.getEncoder().encode(signatureBytes);
            return new String(signatureBytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new IllegalStateException(e);
        }
    }
    private static String getAuthorizationParam(String key, String value) {
        if (key.isEmpty() || value.isEmpty()) {
            return null;
        }
        return key + "=" + "\"" + value + "\"";
    }

}
